using System;
using System.Text;
using System.Globalization;
using System.Collections.Generic;
using System.Security.Cryptography;

namespace MSNClient
{
    class MD5_Digets
    {
        /*
         * 
         * 
         * Windows Live Messenger 8.5.1288.816
         * Product Key = ILTXC!4IXB5FB*PX
         * Product ID = PROD0119GSJUC$18 
         * 
         * 
         * */

        
        public const String ProductKey = "C1BX{V4W}Q3*10SM";
        public const String ProductID = "PROD0120PW!CCV9@";
        public static String BuildChallenge(String aChlData)
        {
            Console.WriteLine("MD5 Digest Test");

            MD5 md5 = MD5.Create();

            long[] keyHashArr = new long[4];
            long[] idChlArr;

            String strChl = aChlData + ProductID;
            strChl = strChl.PadRight(strChl.Length + (8 - strChl.Length % 8), '0');

            byte[] chlString = Encoding.UTF8.GetBytes(strChl);
            idChlArr = new Int64[chlString.Length / 4];

            byte[] md5hash = md5.ComputeHash(Encoding.UTF8.GetBytes(aChlData + ProductKey));
            String md5HashStr = "";
            for (int i = 0; i < md5hash.Length; i++)
            {
                md5HashStr += String.Format("{0:X2}", md5hash[i]);
            }

            for (int g = 0; g < keyHashArr.Length; g++)
            {
                keyHashArr[g] = Int64.Parse(String.Format("{3:X2}{2:X2}{1:X2}{0:X2}", md5hash[g * 4], md5hash[g * 4 + 1], md5hash[g * 4 + 2], md5hash[g * 4 + 3] & 0x7F), NumberStyles.HexNumber);
            }
            for (int j = 0; j < chlString.Length / 4; j++)
            {
                idChlArr[j] = Int64.Parse(String.Format("{3:X2}{2:X2}{1:X2}{0:X2}", chlString[j * 4], chlString[j * 4 + 1], chlString[j * 4 + 2], chlString[j * 4 + 3]), NumberStyles.HexNumber);
            }
            long high = 0;
            long low = 0;
            long temp = 0;
            for (int i = 0; i < idChlArr.Length; i = i + 2)
            {
                temp = idChlArr[i];
                temp = (0x0E79A9C1 * temp) % 0x7FFFFFFF;
                temp += high;
                temp = keyHashArr[0] * temp + keyHashArr[1];
                temp = temp % 0x7FFFFFFF;

                high = idChlArr[i + 1];
                high = (high + temp) % 0x7FFFFFFF;
                high = keyHashArr[2] * high + keyHashArr[3];
                high = high % 0x7FFFFFFF;

                low = low + high + temp;
            }

            high = (high + keyHashArr[1]) % 0x7FFFFFFF;
            low = (low + keyHashArr[3]) % 0x7FFFFFFF;

            SwapBytes(ref high);
            SwapBytes(ref low);

            // Gives high = 0x69A5A771 (1772463985 decimal) and low = 0xD4020628 (3556902440 decimal)
            long key = (high << 32) + low;

            String newChl = "";
            newChl += String.Format("{0:X2}", Int64.Parse(md5HashStr.Substring(0, 16), NumberStyles.HexNumber) ^ key);
            newChl += String.Format("{0:X2}", Int64.Parse(md5HashStr.Substring(16, 16), NumberStyles.HexNumber) ^ key);
            return newChl.ToLower();
        }
        static protected void SwapBytes(ref long aData)
        {
            Int64 tmp = 0;
            for (int i = 0; i < 4; i++)
            {
                tmp += (byte)aData;
                //after adding last byte we do not need offseting
                if (i < 3)
                    tmp = tmp << 8;
                aData = aData >> 8;
            }
            aData = tmp;
        }
    }
}
